package org.amj.wn.admin.analytics;

import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import org.amj.wn.Property;
import org.amj.wn.sect.SectWaqfeNau;

import com.google.appengine.api.datastore.Query.FilterOperator;

public class AnalyticeService {
	public static long getLastUpdated() {
		return AnalyticsCache.getInstance().getLastUpdated();
	}

	public static Map<Object, Integer> analyze(Property filterProperty, FilterOperator operator, String filterValue, Property analysisOn) {
		Map<Object, Integer> result = new TreeMap<Object, Integer>(comparator());

		Row[] rows = AnalyticsCache.getInstance().getTable().getRows();

		for (Row row : rows) {
			if(rowQualifies(row, filterProperty, operator, filterValue)) {
				Object key = row.getData()[analysisOn.ordinal()];
				key = key == null ? "" : key;
				Integer value = result.get(key);
				if(value == null) {
					result.put(key, 1);
				} else {
					result.put(key, value + 1);
				}
			}
		}

		return result;
	}

	public static Map<Object, Integer> scopedAnalyze(String sectId, Property filterProperty, FilterOperator operator, String filterValue, Property analysisOn) {
		
		SectWaqfeNau sectWaqfeNau = SectWaqfeNau.get(sectId);
		
		Map<Object, Integer> result = new TreeMap<Object, Integer>(comparator());

		Row[] rows = AnalyticsCache.getInstance().getTable().getRows();

		for (Row row : rows) {
			if(isRowInScope(sectWaqfeNau, row) && rowQualifies(row, filterProperty, operator, filterValue)) {
				Object key = row.getData()[analysisOn.ordinal()];
				key = key == null ? "" : key;
				Integer value = result.get(key);
				if(value == null) {
					result.put(key, 1);
				} else {
					result.put(key, value + 1);
				}
			}
		}

		return result;
	}
	
	private static boolean rowQualifies(Row row, Property filterProperty,
			FilterOperator operator, String filterValue) {
		if(filterProperty == null)
			return true;
		
		//only =, <= and => are supported
		Object value = row.getData()[filterProperty.ordinal()];
		value = value == null ? "" : value;
		
		if(filterValue !=  null) {
			if(operator == FilterOperator.EQUAL) {
				return filterValue.equals(value);
			} else if (operator == FilterOperator.LESS_THAN_OR_EQUAL) {
				if(areNumbers(value, filterValue)) {
					return toNumber(value) <= toNumber(filterValue);
				} else {
					return compare(value, filterValue) <= 0;
				}
				
			} else if (operator == FilterOperator.GREATER_THAN_OR_EQUAL) {
				if(areNumbers(value, filterValue)) {
					return toNumber(value) >= toNumber(filterValue);
				} else {
					return compare(value, filterValue) >= 0;
				}
				
			} else {
				return false;
			}
		}
		
		return true;
	}

	private static int compare(Object o1, Object o2) {
		if(o1 instanceof Comparable && o1 instanceof Comparable) {
			return ((Comparable)o1).compareTo(o2);
		}
		return 0;
	}

	private static int toNumber(Object filterValue) {
		
		return Integer.parseInt(filterValue.toString());
	}

	private static boolean areNumbers(Object value, String filterValue) {
		try {
			Integer.parseInt(filterValue);
			Integer.parseInt(value.toString());
		} catch (Exception exp) {
			return false;
		}
		return true;
	}

	public static Set<Object> possibleValues(Property filterProperty) {
		Set<Object> values = new TreeSet<Object>(comparator());

		Row[] rows = AnalyticsCache.getInstance().getTable().getRows();
		for (Row row : rows) {
			Object value = row.getData()[filterProperty.ordinal()];
			values.add(value == null ? "" : value);
		}
		return values;
	}

	public static Set<Object> scopedPossibleValues(String sectId, Property filterProperty) {
		
		SectWaqfeNau sectWaqfeNau = SectWaqfeNau.get(sectId);
		
		Set<Object> values = new TreeSet<Object>(comparator());

		Row[] rows = AnalyticsCache.getInstance().getTable().getRows();
		for (Row row : rows) {
			if(isRowInScope(sectWaqfeNau, row)) {
				Object value = row.getData()[filterProperty.ordinal()];
				values.add(value == null ? "" : value);
			}
		}
		return values;
	}

	private static boolean isRowInScope(SectWaqfeNau sectWaqfeNau, Row row) {
		return sectWaqfeNau.getJamaat().equals(row.getData()[Property.jamaatCode.ordinal()]);
	}

	private static Comparator<Object> comparator() {
		return new Comparator<Object>() {

			@Override
			public int compare(Object o1, Object o2) {
				if(o1 instanceof Comparable && o1 instanceof Comparable) {
					return ((Comparable)o1).compareTo(o2);
				}
				return 0;
			}
		};
	}

}
